package com.md.common.utils.reflect;

import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang3.StringUtils;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.*;
import java.text.SimpleDateFormat;
import java.util.*;


/**
 *
 * 反射工具函数集合
 * 提供访问私有变量, 获取泛型类型 Class, 提取集合中元素属性等 Utils 函数
 *
 *  @author lujianping@kuparts.com
 * @create 2017-04-12 11:08
 */
public class ReflectionUtils {
    /**
     * 将反射时�?查查异常转换为运行时异常
     * @return
     */
    public static IllegalArgumentException convertToUncheckedException(Exception ex){
        if(ex instanceof IllegalAccessException || ex instanceof IllegalArgumentException
                || ex instanceof NoSuchMethodException){
            throw new IllegalArgumentException("反射异常", ex);
        }else{
            throw new IllegalArgumentException(ex);
        }
    }
    /**
     * 将�?�转换为指定类型�?
     * @param value:  待转换的�?
     * @param toType: 转换的目标类型Class, 可以是基本数据类型的包装类或指定格式日期�?
     * @return
     */
    public static Object convertValue(Object value, Class<?> toType){
        try {
           /* DateConverter dc = new DateConverter();

            dc.setUseLocaleFormat(true);
            dc.setPatterns(new String[]{"yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss"});

            ConvertUtils.register(dc, Date.class); */

            return ConvertUtils.convert(value, toType);
        } catch (Exception e) {
            e.printStackTrace();
            throw convertToUncheckedException(e);
        }
    }

    /**
     * 将�?�转换为指定类型�?
     * @param value:  待转换的�?
     * @return
     */
    public static Object convertValue(Object value, Field destField){
        try {
            Class destFieldType = destField.getType();
            if(value.getClass() == Date.class){
                if(destFieldType != String.class){
                    return null;
                }
                // 时间日期转换成字符串
                // 获取目标时间日期字段
                DateTimeFormat dateTimeFormat = destField.getAnnotation(DateTimeFormat.class);
                if(dateTimeFormat == null || StringUtils.isEmpty(dateTimeFormat.pattern())){
                    return null;
                }
                String pattern = dateTimeFormat.pattern();
                value = format((Date)value,pattern);
            }
            return ConvertUtils.convert(value, destFieldType);
        } catch (Exception e) {
            e.printStackTrace();
            throw convertToUncheckedException(e);
        }
    }

    /**
     * 提取集合中的对象的属�?(通过 getter 方法), 组成 List
     * @param collection: 来源集合
     * @param propertyName: 要提取的属�?�名�?
     * @return
     */
    @SuppressWarnings("unchecked")
    public static List fetchElementPropertyToList(Collection collection, String propertyName){
        List list = new ArrayList();

        try {
            for(Object obj: collection){
                list.add(PropertyUtils.getProperty(obj, propertyName));
            }
        } catch (Exception e) {
            e.printStackTrace();
            convertToUncheckedException(e);
        }

        return list;
    }

    /**
     * 提取集合中的对象属�??(通过 getter 函数), 组合成由分隔符分隔的字符�?
     * @param collection: 来源集合
     * @param propertyName: 要提取的属�?�名
     * @param seperator: 分隔�?
     * @return
     */
    @SuppressWarnings("unchecked")
    public static String fetchElementPropertyToString(Collection collection, String propertyName,
            String seperator){
        List list = fetchElementPropertyToList(collection, propertyName);
        return StringUtils.join(list, seperator);
    }

    /**
     * 通过反射, 获得定义 Class 时声明的父类的泛型参数的类型
     * @param clazz
     * @param index
     * @return
     */
    @SuppressWarnings("unchecked")
    public static Class getSuperClassGenricType(Class clazz, int index){
        Type genType = clazz.getGenericSuperclass();

        if(!(genType instanceof ParameterizedType)){
            return Object.class;
        }

        Type [] params = ((ParameterizedType)genType).getActualTypeArguments();

        if(index >= params.length || index < 0){
            return Object.class;
        }

        if(!(params[index] instanceof Class)){
            return Object.class;
        }

        return (Class) params[index];
    }

    /**
     * 通过反射, 获得 Class 定义中声明的父类的泛型参数类�??
     * @param <T>
     * @param clazz
     * @return
     */
    @SuppressWarnings("unchecked")
    public static<T> Class<T> getSuperGenericType(Class clazz){
        return getSuperClassGenricType(clazz, 0);
    }

    /**
     * 循环向上转型, 获取对象的DeclaredMethod
     * @param object
     * @param methodName
     * @param parameterTypes
     * @return
     */
    public static Method getDeclaredMethod(Object object, String methodName, Class<?>[] parameterTypes){

        for(Class<?> superClass = object.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()){
            try {
                //superClass.getMethod(methodName, parameterTypes);
                return superClass.getDeclaredMethod(methodName, parameterTypes);
            } catch (NoSuchMethodException e) {
                //Method 不在当前类定�??, 继续向上转型
            }
        }

        return null;
    }

    /**
     * 将filed变为可访�?
     * @param field
     */
    public static void makeAccessible(Field field){
        if(!Modifier.isPublic(field.getModifiers())){
            field.setAccessible(true);
        }
    }

    /**
     * 循环向上转型, 获取对象的DeclaredField
     * @param object
     * @param filedName
     * @return
     */
    public static Field getDeclaredField(Object object, String filedName){
        return getDeclaredField(object.getClass(), filedName);
    }

    /**
     * 循环向上转型, 获取对象的DeclaredField
     * @param clazz
     * @param filedName
     * @return
     */
    public static Field getDeclaredField(Class clazz, String filedName){

        for(Class<?> superClass = clazz;
            superClass != Object.class; superClass = superClass.getSuperclass()){
            try {
                return superClass.getDeclaredField(filedName);
            } catch (NoSuchFieldException e) {

            }
        }
        return null;
    }

    /**
     * 直接调用对象方法, 而忽略修饰符(private, protected)
     * @param object
     * @param methodName
     * @param parameterTypes
     * @param parameters
     * @return
     * @throws InvocationTargetException
     * @throws IllegalArgumentException
     */
    public static Object invokeMethod(Object object, String methodName, Class<?> [] parameterTypes,
            Object [] parameters) throws InvocationTargetException{

        Method method = getDeclaredMethod(object, methodName, parameterTypes);

        if(method == null){
            throw new IllegalArgumentException("Could not find method [" + methodName + "] on target [" + object + "]");
        }

        method.setAccessible(true);

        try {
            return method.invoke(object, parameters);
        } catch(IllegalAccessException e) {

        }

        return null;
    }

    /**
     * 直接设置对象属�?��??, 忽略 private/protected 修饰�?, 也不经过 setter
     * @param object
     * @param fieldName
     * @param value
     */
    public static void setFieldValue(Object object, String fieldName, Object value){
        Field field = getDeclaredField(object, fieldName);

        if (field == null)
            throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + object + "]");

        makeAccessible(field);

        try {
            field.set(object, value);
        } catch (IllegalAccessException e) {

        }
    }

    /**
     * 直接读取对象的属性�??,忽略 private/protected 修饰�?,也不经过 getter
     * @param object
     * @param fieldName
     * @return
     */
    public static Object getFieldValue(Object object, String fieldName){
        Field field = getDeclaredField(object, fieldName);
        if(field == null){
            return null;
        }

//        if (field == null)
//            throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + object + "]");

        makeAccessible(field);

        Object result = null;

        try {
            result = field.get(object);
        } catch (IllegalAccessException e) {

        }

        return result;
    }

    /**
     * �?查属性是否存
     * @param entityClass
     * @param fieldName
     * @return
     */
    public static boolean checkFieldIsExist(Class entityClass,String fieldName) {
//			entityClass.getDeclaredField(fieldName);
        Field field = getDeclaredField(entityClass,fieldName);
        return field != null ? true : false;
    }

    /**
     * 设置值如果�?�不为空
     * @param obj
     * @param fieldName
     * @param value
     */
    public static void setValueNotEmpty(Object obj,String fieldName,String value) {
    	if(StringUtils.isNotEmpty(value)) {
	    	if(checkFieldIsExist(obj.getClass(), fieldName)) {
	    		Object convertValue=convertValue(value,getDeclaredField(obj, fieldName).getType());
	    		setFieldValue(obj, fieldName, convertValue);
	    	}
    	}
    }

    /**
     * 设置值如果�?�不为空
     * @param obj
     * @param fieldName
     * @param value
     */
    public static void setValueNotEmpty(Object obj,String fieldName,Object value) {
        if(value != null) {
            if(checkFieldIsExist(obj.getClass(), fieldName)) {
                Object convertValue=convertValue(value,getDeclaredField(obj, fieldName).getType());
                setFieldValue(obj, fieldName, convertValue);
            }
        }
    }

    /**
     * 同名属�?�复�?
     * @param destType 目标对象的类�?
     * @param sourceObj 源对�?
     * @return 转化得到的目标类型对�?
     * @throws IntrospectionException
     *             如果分析类属性失�?
     * @throws IllegalAccessException
     *             如果实例化JavaBean 失败
     * @throws InstantiationException
     *             如果实例化JavaBean 失败
     * @throws InvocationTargetException
     *             如果调用属�?�的 setter 方法失败
     */
    public static Object convertBean(Class destType, Object sourceObj)
            throws IntrospectionException, IllegalAccessException,
            InstantiationException, InvocationTargetException {
        BeanInfo beanInfo = Introspector.getBeanInfo(destType); // 获取类属�?
        Object destObj = destType.newInstance(); // 创建JavaBean对象

        // 给JavaBean对象的属性赋�?
        PropertyDescriptor[] propertyDescriptors =  beanInfo.getPropertyDescriptors();
        for (int i = 0; i< propertyDescriptors.length; i++) {
            PropertyDescriptor descriptor = propertyDescriptors[i];
            String propertyName = descriptor.getName();
            Object sourceFieldValue = getFieldValue(sourceObj, propertyName);
            if (sourceFieldValue != null) {
                Object value = convertValue(sourceFieldValue, descriptor.getPropertyType());
                Object[] args = new Object[1];
                args[0] = value;

                descriptor.getWriteMethod().invoke(destObj, args);
            }
        }
        return destObj;
    }

    /**
     * 将JavaBean 对象转化为Map
     * @param bean 要转化的JavaBean对象
     * @return 转化出来的Map 对象
     * @throws IntrospectionException 如果分析类属性失�?
     * @throws IllegalAccessException 如果实例化JavaBean失败
     * @throws InvocationTargetException 如果调用属�?�的setter方法失败
     */
    public static Map convertBean(Object bean)
            throws IntrospectionException, IllegalAccessException, InvocationTargetException {
        Class type = bean.getClass();
        Map returnMap = new HashMap();
        BeanInfo beanInfo = Introspector.getBeanInfo(type);

        PropertyDescriptor[] propertyDescriptors =  beanInfo.getPropertyDescriptors();
        for (int i = 0; i< propertyDescriptors.length; i++) {
            PropertyDescriptor descriptor = propertyDescriptors[i];
            String propertyName = descriptor.getName();
            if (!propertyName.equals("class")) {
                Method readMethod = descriptor.getReadMethod();
                Object result = readMethod.invoke(bean, new Object[0]);
                if (result != null) {
                    returnMap.put(propertyName, result);
                }
            }
        }
        return returnMap;
    }

    /**
     * 按指定的索引模型将实体转换成solr doc
     * @param dataObj �?要被转换的数据实体对�?
     * @param docClazz 索引模型�?
     * @return
     * @throws IntrospectionException
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     */
    /*
    public static SolrInputDocument convert2SolrDoc(Object dataObj,Class<?> docClazz)
            throws IntrospectionException, IllegalAccessException, InvocationTargetException,IllegalArgumentException {
        if(dataObj == null){
            throw new IllegalArgumentException("dataObj is null");
        }
        Field[] fields = docClazz.getDeclaredFields();
        Field[] superClassFields = null;

        if(fields != null && fields.length > 0){
            List<Field> fieldList = Arrays.asList(fields);
            // 合并父类的属�?
            if(superClassFields != null && superClassFields.length > 0){
                fieldList.addAll(Arrays.asList(superClassFields));
            }
            SolrInputDocument solrInputDocument = new SolrInputDocument();
            for(Field field : fieldList){
                NotConvert notDocField = field.getAnnotation(NotConvert.class);
                if(notDocField == null || notDocField.value() == false){
                    Object dataFieldValue = getFieldValue(dataObj,field.getName());
                    if(dataFieldValue != null){
                        Object value = convertValue(dataFieldValue, field.getType());
                        solrInputDocument.addField(field.getName(),value);
                    }
                }
            }
            return solrInputDocument;
        }
        return null;
    }
    */

    /**
     * 获取类的字段列表
     * @param clazz 资源列表来源�?
     * @return
     */
    public static List<Field> getDeclareFields(Class clazz){
        if(clazz == null){
            throw new IllegalArgumentException("object class is null");
        }

        List<Field> fieldList = new ArrayList<>();

        Field[] fields = clazz.getDeclaredFields();
        if(fields != null){
            for(Field field : fields){
                fieldList.add(field);
            }
        }
        Class superClass = clazz.getSuperclass();
        if(superClass != Object.class){
            Field[] superClassFields = superClass.getDeclaredFields();
            if(superClassFields != null){
                for(Field field : superClassFields){
                    fieldList.add(field);
                }
            }
        }
        return fieldList;
    }

    /**
     * 从solr索引文档转换得到索引模型类对�?
     * <p>
     *     仅支持基本数据类型包括字符串的属性复制�??
     * </p>
     * @param destDocSchemaClazz 目标索引模型�?
     * @param solrDocument solr索引文档
     * @return
     * @throws IntrospectionException
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     * @throws IllegalArgumentException
     * @throws InstantiationException
     */
    /*
    public static Object fromSolrDoc(Class<?> destDocSchemaClazz,SolrDocument solrDocument)
            throws IntrospectionException, IllegalAccessException, InvocationTargetException, IllegalArgumentException, InstantiationException {
        if(solrDocument == null){
            throw new IllegalArgumentException("solrDocument is null");
        }
        BeanInfo beanInfo = Introspector.getBeanInfo(destDocSchemaClazz); // 获取类属�?
        Object destObj = destDocSchemaClazz.newInstance(); // 创建JavaBean对象
        // 给JavaBean对象的属性赋�?
        PropertyDescriptor[] propertyDescriptors =  beanInfo.getPropertyDescriptors();
        for (int i = 0; i< propertyDescriptors.length; i++) {
            PropertyDescriptor descriptor = propertyDescriptors[i];
            NotDocField notDocField = descriptor.getPropertyType().getAnnotation(NotDocField.class);
            if(notDocField == null || notDocField.value() == false){
                String propertyName = descriptor.getName();
                Object sourceFieldValue = solrDocument.get(propertyName);
                if (sourceFieldValue != null) {
                    Object value = convertValue(sourceFieldValue, descriptor.getPropertyType());
                    Object[] args = new Object[1];
                    args[0] = value;

                    descriptor.getWriteMethod().invoke(destObj, args);
                }
            }
        }
        return destObj;
    }
    */

    /**
     * 属�?�复�?
     * <p>
     *     仅支持基本数据类型包括字符串的属性复制�??
     * </p>
     * @param destObj
     *          �?要赋值目标对�?
     * @param sourceObj
     *          �?要获取属性�?�的源对�?
     * @return
     */
    public static void copyProperties(Object destObj, Object sourceObj) {
        if(destObj == null){
            throw new IllegalArgumentException("destObj is null");
        }
        if(sourceObj == null){
            throw new IllegalArgumentException("sourceObj is null");
        }

        List<Field> fieldList = getDeclareFields(destObj.getClass());
        for (Field field : fieldList) {
            IgnoreCopy ignoreCopy = field.getAnnotation(IgnoreCopy.class);
            if(ignoreCopy == null || ignoreCopy.value() == false){
                String propertyName = field.getName();
                Object sourceFieldValue = getFieldValue(sourceObj, propertyName);
                if (sourceFieldValue != null) {
                    Object value = convertValue(sourceFieldValue, field.getType());
                    Object[] args = new Object[1];
                    args[0] = value;
                    setValueNotEmpty(destObj, propertyName, value);
                }
            }
        }
    }

    public static String format(Date date,String pattern){
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);
        return simpleDateFormat.format(date);
    }

    /*
    public static void main(String[] args) throws IllegalAccessException, InvocationTargetException, IntrospectionException, InstantiationException {
		UserDto user = new UserDto();
		user.setAddress("aaa");
		user.setUserId(111L);
		user.setMobile("12345679");
		Map map = convertBean(user);
		System.out.println(map.toString());
		UserDto u = (UserDto) convertMap(UserDto.class, map);
		System.out.println(u.toString());

	}*/
}
